﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Xml;
using SharePointBrowser.ListsWS;
using SharePointBrowser.WebsWS;
using SharePointBrowser.SiteDataWS;

namespace SharePointBrowser.Data
{
    internal class SPAdapter
    {
        private static SPAdapter instance = new SPAdapter();
        private XmlNamespaceManager namespaceManager;

        public NetworkCredential Credentials { get; set; }
        public string SiteUrl { get; set; }

        private SPAdapter()
        {
            namespaceManager = new XmlNamespaceManager(new XmlDocument().NameTable);
            namespaceManager.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/soap/");
            namespaceManager.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
            namespaceManager.AddNamespace("z", "#RowsetSchema");
        }

        public static SPAdapter Instance
        {
            get { return instance; }
        }

        public XmlNamespaceManager NamespaceManager
        {
            get { return namespaceManager; }
        }

        public XmlNode GetListCollection()
        {
            return GetListCollection(SiteUrl);
        }

        public XmlNode GetListCollection(string siteUrl)
        {
            using (Lists srvLists = new Lists())
            {
                srvLists.Url = siteUrl.TrimEnd('/') + "/_vti_bin/lists.asmx";
                srvLists.Credentials = Credentials;
                return srvLists.GetListCollection();
            }
        }

        public XmlNode GetWebCollection()
        {
            return GetWebCollection(SiteUrl);
        }

        public XmlNode GetWebCollection(string siteUrl)
        {
            using (Webs srvWebs = new Webs())
            {
                srvWebs.Url = siteUrl.TrimEnd('/') + "/_vti_bin/webs.asmx";
                srvWebs.Credentials = Credentials;
                return srvWebs.GetWebCollection();
            }
        }

        public XmlNode GetWeb(string webUrl)
        {
            using (Webs srvWebs = new Webs())
            {
                srvWebs.Url = webUrl.TrimEnd('/') + "/_vti_bin/webs.asmx";
                srvWebs.Credentials = Credentials;
                return srvWebs.GetWeb(webUrl);
            }
        }

        public XmlNode GetList(string listName)
        {
            return GetList(listName, SiteUrl);
        }

        public XmlNode GetList(string listName, string siteUrl)
        {
            using (Lists srv = new Lists())
            {
                srv.Url = siteUrl.TrimEnd('/') + "/_vti_bin/Lists.asmx";
                srv.Credentials = Credentials;
                return srv.GetList(listName);
            }
        }

        public XmlNode GetListItems(string listId, string folder)
        {
            return GetListItems(listId, folder, SiteUrl);
        }

        public XmlNode GetListItems(string listId, string folder, string siteUrl)
        {
            using (Lists srv = new Lists())
            {
                srv.Url = siteUrl.TrimEnd('/') + "/_vti_bin/Lists.asmx";
                srv.Credentials = Credentials;
                XmlDocument xmlDoc = new XmlDocument();
                XmlNode queryNode = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
                XmlNode viewNode = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
                XmlNode optionsNode = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
                optionsNode.InnerXml = "<Folder>" + folder + "</Folder>";
                return srv.GetListItems(listId, null, queryNode, viewNode, null, optionsNode, null);
            }
        }

        public XmlNode GetListItemById(string listId, string itemId, string siteUrl)
        {
            using (Lists srv = new Lists())
            {
                srv.Url = siteUrl.TrimEnd('/') + "/_vti_bin/Lists.asmx";
                srv.Credentials = Credentials;
                XmlDocument xmlDoc = new XmlDocument();
                XmlNode queryNode = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
                queryNode.InnerText = "<Where><Eq><FieldRef Name=\"ID\" /><Value Type=\"Counter\">" + itemId + "</Value></Eq></Where>";
                XmlNode viewNode = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
                XmlNode optionsNode = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
                optionsNode.InnerText = "<ViewAttributes Scope=\"Recursive\" />";
                return srv.GetListItems(listId, null, queryNode, viewNode, "1", optionsNode, null);
            }
        }

        public void AddNewItem(string listName, IEnumerable<KeyValuePair<string, string>> fieldValues, string siteUrl)
        {
            using (Lists srv = new Lists())
            {
                srv.Url = siteUrl.TrimEnd('/') + "/_vti_bin/Lists.asmx";
                srv.Credentials = Credentials;
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml("<Batch OnError='Continue'>" +
                    "<Method ID='1' Cmd='New'>" +
                        fieldValues.Aggregate<KeyValuePair<string, string>, string, string>(
                            "<Field Name='ID'>New</Field>",
                            (seed, val) => seed + "<Field Name='" + val.Key + "'>" + val.Value + "</Field>",
                            val => val) +
                    "</Method>" +
                    "</Batch>");
                XmlNode updates = xmlDoc.SelectSingleNode("//Batch");
                XmlNode results = srv.UpdateListItems(listName, updates);
                XmlNode result = results.SelectSingleNode("sp:Result[@ID='1,New']", SPAdapter.Instance.NamespaceManager);
                if (result != null)
                {
                    XmlNode errorCode = result.SelectSingleNode("sp:ErrorCode", SPAdapter.Instance.NamespaceManager);
                    if (errorCode != null && errorCode.InnerText != "0x00000000")
                    {
                        XmlNode errorText = result.SelectSingleNode("sp:ErrorText", SPAdapter.Instance.NamespaceManager);
                        throw new Exception(errorText != null ? errorText.InnerText : "An error has occured, code: " + errorCode.InnerText);
                    }
                }
            }
        }
    }
}
